feat(word): add tracked changes find/replace support#71
Open
kuishou68 wants to merge 674 commits into
Open
Conversation
Closes 4 CodeQL js/incomplete-sanitization alerts (lines 58, 64, 119, 121). Aligns with the existing pattern at lines 73, 94, 379.
Refactor Excel row sort to follow the region-action convention used by
merge: the sort range is now encoded in the path (/Sheet1/A1:C100) rather
than a separate --prop range=... Sheet-level path auto-detects the used
range and delegates to the same SortRangeRows helper.
Correctness fixes folded into the rewrite:
- precise column-letter match (old StartsWith('A') misfired on AA)
- raw CellValue comparison (not display-formatted text) so numeric keys
compare as doubles even when the cell has a format code
- first sort key uses OrderBy (was ThenBy on a no-op identity)
- per-row sort-key materialization (was O(rows × keys × cells/row))
- reject ranges intersecting merged cells (was silent corruption)
- reject ranges with shared-formula groups (was broken ref anchors)
- sortState placed after SheetData; sortCondition@ref scoped to the key
column within the sort range
New sheet-level contract:
set data.xlsx /Sheet1/A1:C100 --prop sort='A asc, B desc' --prop sortHeader=true
set data.xlsx /Sheet1 --prop sort='A asc' (auto-detect)
Repro: set xxx.xlsx '/Sheet1/A1:A3' --prop sort='' Before: silently succeeded, no sort applied, no error. After: throws ArgumentException 'sort value cannot be empty'. Sheet-level sort='' keeps its clear-sortState semantics (handled by the sheet-level dispatcher before reaching SortRangeRows); the throw fires only when the empty value arrives via a range path.
Repro: set xxx.xlsx '/Sheet1/A1:B10' --prop sort='C asc' Before: silently succeeded with no reorder and wrote a malformed sortCondition ref pointing outside sortState@ref. After: throws ArgumentException 'Sort column C is outside the range A:B'. The check runs after each key is parsed in SortRangeRows, using the normalized col1/col2 bounds.
Repro: set xxx.xlsx '/Sheet1/A3:A1' --prop sort='A asc' Before: row1>row2 scan produced an empty rowsInRange, data was not reordered, and sortState@ref was written as the literal reversed 'A3:A1'. After: SortRangeRows swaps col1/col2 and row1/row2 when they arrive in max:min order. Rows reorder correctly and sortState@ref is well-formed.
Repro: set xxx.xlsx '/Sheet1/A1:B10' --prop 'sort=A asc B' Before: parsed as (A, asc) and silently dropped the trailing 'B'. After: throws ArgumentException 'Invalid sort key ...: too many tokens. Expected <col> [asc|desc]'. Applies per comma-separated key entry, so 'A asc, B desc extra' also fails loudly on the second key.
Before: WriteSortState always did sheetData.InsertAfterSelf(sortState). When an autoFilter was present on the sheet, this produced the child order [sheetData, sortState, autoFilter], which violates the CT_Worksheet schema (autoFilter must precede sortState). After: if an autoFilter child exists, insert sortState immediately after it; otherwise keep the existing 'after sheetData' placement.
Repro: set A1=30 A2=10 B1==A1+1000 B2==A2+1000 set A1:B2 sort='A asc' -> B1 silently became =A2+1000 (stale ref to old row) Cause: SortRangeRows rewrites Cell.CellReference to the new row index but leaves CellFormula.Text encoding the *old* relative addresses, so Excel recalculates against wrong refs and silently produces wrong values. Data-corruption class. Fix: extend the existing shared-formula reject to cover any cell with a CellFormula in the data rows (CONSISTENCY(sort-rejects-formulas)). A full ref-rewrite (handling A1/\$A\$1/A:B/Sheet!A1/named ranges) is high risk for partial-correctness regressions and deferred to v2. Known limitation: does not catch formulas *outside* the sort range that reference cells *inside* it; same scope as the shared-formula check.
Repro: a sheet where SheetData lists rows out of RowIndex order (e.g. <row r="3">, <row r="1">, <row r="2">) — legitimate output from some writers or malformed edits. SortRangeRows built originalIndices from List position (document order), so the sorted data was mapped onto scrambled target row numbers, producing a wrong arrangement. Fix: OrderBy(v => v) on originalIndices so sorted slots are always assigned in ascending row order regardless of SheetData layout.
Repro: a sheet containing mergeCells but no autoFilter. After a sort, WriteSortState fell back to sheetData.InsertAfterSelf(ss), producing a child sequence of sheetData → sortState → mergeCells which violates CT_Worksheet (sortState must precede mergeCells? No — sortState sits AFTER autoFilter and BEFORE mergeCells per ECMA-376). Strict validators reject the document; Excel silently ignores the sortState. Fix: thread sortState into its correct schema slot by walking the predecessors in reverse (autoFilter, scenarios, protectedRanges, sheetProtection, sheetCalcPr, sheetData) and InsertAfterSelf the nearest present anchor. This places sortState between its nearest predecessor and any successor (mergeCells, conditionalFormatting, hyperlinks, etc.).
…plicates)
Repro: two related malformed inputs that silently corrupted sort output.
(a) A <row> element without a RowIndex attribute — the range filter
`r.RowIndex?.Value >= dataStartRow` silently dropped that row, so
the row's data survived sort but was reassigned a stale RowIndex,
losing the user's data alignment.
(b) Two <row r="N"> entries with the same RowIndex — sort wrote two
rows into the same target slot, silently dropping one.
Fix: at SortRangeRows entry, scan SheetData once and throw
InvalidOperationException on either condition. Sorting a corrupted
layout should surface the corruption, not silently paper over it.
…t=false Repro: a sheet with sheetProtection@sheet="true" could still be sorted silently, mutating a worksheet the author explicitly protected. The sort also leaves the protection in place, so the user's next interactive sort in Excel will be blocked — masking the breach. Fix: at SortRangeRows entry, check the sheet's SheetProtection. If @sheet is true and @sort is absent/true (OOXML default: sort IS protected), throw InvalidOperationException. Honor the escape hatch @sort="false", which per spec means "sort is excluded from the protected operation set" — allow the sort in that case so we do not regress legitimate workflows.
Replace per-cell inset box-shadow with a single absolutely-positioned overlay div sized to the union rect of the selected cells. The previous approach drew the selection frame via inset box-shadow, which rendered visibly offset from the cell's visual edge in border-collapse tables because adjacent cells share a 1px border and shadow positioning is relative to the padding box, not the shared border edge. The overlay anchors inside the table so it scrolls with content automatically; a scroll/resize listener handles edge cases.
Previously sort refused the operation when any formula lived anywhere on a row that overlapped the sort range, and when any row in the whole sheet had a duplicate RowIndex. Both checks were over-broad: - Formulas in columns outside the sort column range are unaffected by sort (the formula text and its refs stay intact even if the row moves). - Duplicate RowIndex rows outside the sort row range cannot cause the sort step to lose or misplace data. Narrow both checks to cells/rows that actually intersect the sort range. Missing RowIndex is still always rejected because such a row cannot be located in any range and risks silent drop by the sort scan.
…ll refs ParseCellReference previously used int.Parse on the row portion of a cell reference, which threw OverflowException on malformed inputs like "A4294967295" (uint.MaxValue). The overflow bubbled all the way up as an unhandled numeric exception with no document context. Switch to long.TryParse and fold the range check into the same branch so any row outside 1..1048576 — whether out-of-int-range or merely out-of-Excel-range — produces a consistent ArgumentException with the offending reference included.
double.TryParse("NaN") returns true, producing rank=0 (number), while
double.TryParse("1e999") overflows to +Infinity — also rank=0. The
resulting sort order mixed non-finite doubles with finite numbers in
ways Excel never does; Excel treats NaN / Infinity / -Infinity as
literal strings.
Classify those tokens (and any non-finite parse result) as rank=1
(string) so number/text ordering stays consistent with Excel.
The sheet-properties help block listed 'sort' but not the companion 'sortHeader' flag, even though the Set handler has consumed it since sort landed. Add a one-line description next to sort.
When a caller passes both merge=true and sort=... to set /Sheet1/A1:B3, merge was applied first and wrote MergeCells into the XML, then SortRangeRows rejected the merged region and threw, leaving the file in a half-written state with an unwanted merge persisted. Detect the combo at SetRange entry and throw before any write. Users who need both must split the call. Consistent with the existing 'fail-before-write' precedent (merged-cell reject, formula reject).
…Validations) Row sort rewrote cell CellReference values in <sheetData>, but left sidecar metadata untouched. Hyperlinks, comments, and single-cell dataValidation sqref tokens continued to point at the old row positions — so after sort the hyperlink/comment/validation appeared attached to a different row of data. Capture the old->new row mapping before mutating row indices, then rewrite hyperlink ref, comment ref, and each single-cell token in dataValidation sqref that falls inside the sort rectangle. Refs outside the rectangle and multi-cell range tokens (e.g. A2:A10) that cross the sort boundary are intentionally left untouched — splitting partial ranges would require a more invasive rewrite. Also rename the internal CellInRange helper to CellColumnInSortRange. The name now accurately reflects that the check is column-only; row containment is enforced by the caller iterating rowsInRange.
ConditionalFormatting rules anchored on single cells (e.g. a highlight rule on A2:A2) were left pointing at the pre-sort cell after sort, so the rule followed a row that no longer existed there. Extend the post-sort sidecar rewrite with a ConditionalFormatting branch that mirrors the dataValidations handling: tokenize sqref, skip multi-cell range tokens (same partial-rect scope limitation), and remap each single-cell token inside the sort rectangle via oldToNewRow.
Sorting A1:A1 or a range whose data region collapses to zero/one rows is a logical no-op — there is nothing to reorder. The previous code still wrote sortState in those branches, which made Excel UI show a sort indicator on a range that was never actually sorted. Skip WriteSortState in the two no-op paths so the UI stays honest.
Previously shown as A:asc,B:desc which fails at parse time — actual sort spec is space-separated column+direction, comma-separated for multi-key (e.g. 'Salary desc' or 'Dept asc, Salary desc'). AI agents following the wrong example hit errors on every sort call.
RewriteSidecarRefsAfterSort handled hyperlinks, comments, threaded comments, dataValidations, and conditionalFormatting but ignored DrawingsPart. Pictures, shapes, and charts anchored at a row inside the sort range stayed pinned to the original 0-indexed RowId after the data under them was reordered, leaving them visually attached to the wrong content row. Now TwoCellAnchor and OneCellAnchor FromMarker/ToMarker RowIds are remapped through oldToNewRow alongside the other sidecars. Follows the same partial-rect scoping as dataValidations / conditional formatting: a TwoCellAnchor is remapped only when both From and To rows fall inside the sort rectangle; if the anchor straddles the boundary it is preserved verbatim. OneCellAnchor has only From, so it moves whenever From is inside. Columns are never rewritten because sort only permutes rows. Limitation: anchors straddling the sort rect boundary remain authored-as-is, consistent with how multi-cell dataValidation and CF range tokens are handled.
…e sortState
R7-1: physical sort comparer switched from Ordinal to OrdinalIgnoreCase so
mixed-case keys ("Apple"/"apple") land in an order consistent with the
sortState@caseSensitive=false metadata default and with Excel's own default.
R7-2: RewriteSidecarRefsAfterSort now also rewrites ProtectedRange sqref
(7th sidecar, same cell-anchored scoping as dataValidations / CF). Single-
cell tokens inside the sort rectangle follow row movement; range tokens
and out-of-rect tokens are preserved.
R7-3: all three SortState removal sites (sheet-level clear, range-level
clear, WriteSortState) iterate Elements<SortState>().ToList() instead of
GetFirstChild, so malformed files carrying duplicate sortState children
are fully collapsed to a single (or zero) element.
R7-4 (sortHeader default) rejected again with a CONSISTENCY(sort-header-default)
comment block at the dispatch site documenting the decision history and the
preferred future path (project-wide default flip, not a per-call heuristic
warning).
…leanup - Sheet-level sort case now calls DeleteCalcChainIfPresent, matching the range-level sort path. Without this, a stale calc chain could survive the reorder and expose Excel to a mid-state repair risk on open. - Swap ws.Elements<SortState>() -> ws.Descendants<SortState>() in the three sort-rewrite/clear sites so malformed files that nest <sortState> under <sheetData> are also cleaned on rewrite, instead of leaving the nested one behind and ending with two sortStates.
Add OOXML-compliant dual representation for SVG images: - main a:blip/@r:embed → PNG fallback part (auto or user-supplied) - a:blip/a:extLst asvg:svgBlip → SVG image part Modern Office (2016+) renders the SVG; older viewers see the raster fallback. Introduces Core/SvgImageHelper + SVG dimension parsing in ImageSource so width/height auto-sizing matches PNG/JPEG behavior. Supports 'fallback=<path>' prop on Add to override the 1x1 transparent PNG default. Set (path/src) symmetrically strips/attaches the extension and deletes the orphaned SVG part when replacing across formats.
When user passes a header name like 'Score' as a sort column, the prior
error wording ('Sort column SCORE is outside the range A:B') misled AI
agents into guessing column letters rather than recognizing that header
names are unsupported. Detect column tokens that parse past XFD with
length >= 4 and return a targeted 'Column names are not supported; use
column letters (A, B, AA, up to XFD)' message. Genuine out-of-range
letters (e.g. Z in A:B) still return the original range-error wording.
GetShapeNode now reads <a:xfrm rot/flipH/flipV> on the shape's Transform2D and surfaces them as format.rotation (canonicalized to 0-360 degrees) and format.flip (h/v/both), matching how Add accepts these properties.
…els subkeys Three small CLI-parity gaps surfaced across prior rounds. Word underline= now accepts the full OOXML enum. NormalizeUnderlineValue centralizes the mapping (wavy -> wave, dashdot -> dotDash, plus double / thick / dotted / *Heavy variants / words). Previously the handler rejected wavy despite PPTX accepting it. Word firstLineIndent= accepts unit strings (2cm, 0.5in, 18pt) via SpacingConverter.ParseWordSpacing. Bare twips still work (backward compat). Range check > 31680 twips preserved. Chart datalabels.* sub-keys (showvalue, showpercent, showcatname, showsername, showlegendkey) now emit the corresponding c:show* elements. Registered in DeferredAddKeys so they apply at Add time, not only via post-build Set. ChartHelper is shared across PPTX and Excel so the fix lands on both sides.
Two CLI-surface gaps carried from earlier rounds. Slide HeaderFooter toggle (R16 deferred): set /slide[N] now accepts showFooter, showSlideNumber, showDate, showHeader. Each maps to the corresponding <p:hf ftr/sldNum/dt/hdr> attribute on the slide element (PowerPoint writes <p:hf> directly under <p:sld> when the per-slide "Header & Footer" dialog toggles it, so we match). Picture fill modes (R10 Cleanup-D): add picture --prop fill=... now accepts stretch (default, non-regression), contain, cover, and tile. contain/cover sniff the image's native pixel dimensions via ImageSource.TryGetDimensions and compute <a:fillRect> insets (in thousandths of a percent) — positive for letterbox, negative for crop. tile emits <a:tile sx sy algn flip> with optional tilescale, tilealign, tileflip sub-keys. Falls back to bare stretch when image dimensions can't be sniffed.
Two CLI gaps carried from earlier rounds. AddPlaceholder: `add /slide[N] --type placeholder --prop phType=...` now creates a <p:sp> with <p:ph type="..."/> for any of the known placeholder types (body, date, footer, slidenum, header, subtitle, title, picture, chart, table, diagram, media, obj, clipart). Emits an empty <p:spPr> so geometry and fonts inherit from the layout, plus a minimal <p:txBody> that optionally prepopulates via a text= property. Dispatch wired in Add.cs. Group resize preserves scaling: when `set /slide[N]/group[M] width=` or `height=` / `x=` / `y=` is applied and the group's `TransformGroup.ChildExtents` / `ChildOffset` is null, snapshot the current Extents / Offset into new ChildExtents / ChildOffset BEFORE updating Extents / Offset. This establishes the ext≠chExt baseline that PowerPoint needs to visibly compress the group's children instead of resizing them 1:1 with the container.
…ml anchor Three gaps remaining from R14. Hyperlinks now accept tooltip= alongside link= on both run and shape Add / Set. ApplyRunHyperlink and ApplyShapeHyperlink set the Tooltip attribute on HyperlinkOnClick. link= gains internal-jump support: - slide[N] creates a slide-to-slide relationship and emits action="ppaction://hlinksldjump" - firstslide / lastslide / nextslide / previousslide emit the corresponding ppaction://hlinkshowjump?jump=... (PowerPoint-native) ReadRunHyperlinkUrl resolves both forms back to the original string. HTML preview wraps shape-level hyperlinks in an <a> tag. Shape hlinkClick now lives on nvSpPr/cNvPr (canonical shape location) in addition to runs so the HTML renderer can detect it from the shape tree; runs still carry the inline anchor from R14 so text stays clickable inline. External URLs get a wrapping anchor with rel="noopener" target="_blank"; cursor:pointer affordance added. Internal slide-jump links are intentionally not wrapped (no navigable href in a static HTML preview).
Excel refuses to open files containing cells whose text length exceeds 2^15-1 (0x800A03EC on save/open). Previously Add, Set, and CSV import wrote oversized values silently, corrupting the file. Add EnsureCellValueLength in ExcelHandler.Helpers.cs (internal const MaxCellTextLength = 32767) and call it from the Add cell value path, the Set cell value/text path, and the CSV/TSV import path. Rejection is a clear ArgumentException naming the cell and the observed length.
Setting a cell to an ISO datetime like "2024-03-15T10:30:00" with
type=date previously stored the literal string, so Excel rendered it
as text instead of a real date. Only date-only ("2024-03-15") and
numeric serials were converted.
Centralize the format list as IsoDateFormats + TryParseIsoDateFlexible
in ExcelHandler.Helpers.cs, accepting yyyy-MM-dd, yyyy-MM-ddTHH:mm,
yyyy-MM-ddTHH:mm:ss, yyyy-MM-dd HH:mm:ss, and the ...Z / .fff[Z]
variants. Add and Set cell-value paths route through the shared helper
so every entry point converts to a fractional OADate.
When both value= and formula= are passed to Set or Add on the same
cell, formula wins (it is written last, clearing CellValue). Users who
typo'd or duplicated props previously had no indication the literal
value had been discarded.
Emit a stderr warning ("Both value= and formula= supplied — using
formula, value ignored.") from both the Add cell path and the Set
value/text path so the precedence is visible.
Excel silently drops effectLst children that violate DrawingML schema order (blur → fillOverlay → glow → innerShdw → outerShdw → prstShdw → reflection → softEdge). Previously shape Add appended outerShdw before glow, so a shape with both shadow and glow rendered without the glow halo despite being present in the raw XML. Reorder the shape Add effect build to emit children in schema order, and rewrite Set TrySetShapeEffect to InsertBefore the next-in-order sibling when adding effects incrementally. Same fix applied to the text-level effect block used by fill=none shapes.
Explicit type=string now forces an inline-string cell even when the value is numeric-looking (e.g. value=123), instead of silently storing a number. Explicit type=number with a non-numeric value now throws ArgumentException instead of silently storing as string.
labelRotation= was previously accepted silently but dropped. Wire it onto the target c:catAx / c:valAx via a <c:txPr>/<a:bodyPr rot="N"/> where N is degrees * 60000 (OOXML 60000ths-of-a-degree encoding). Accept bare labelRotation= (both axes) plus xAxis./valAxis./yAxis. labelRotation aliases.
add shape --prop ref=B2 previously mapped to anchor=B2:B2 with identical from/to markers, producing a zero-width/height invisible shape in Excel. Expand single-cell refs to a 1-column x 1-row rectangle (B2 -> B2:C3) so the shape has a visible extent. Range refs (B2:D6) are unchanged.
conditional formatting rule=aboveAverage previously silently dropped the stdDev= and equalAverage= properties. Both now flow onto the cfRule attributes: stdDev=N (integer deviations from mean) and equalAverage=true (include values equal to the mean). Also accepted the aboveaverage= spelling as an alias for above= to mirror the OOXML attribute name.
Set on slicer paths returned 'Element not found' because the Set dispatch fell through to the generic XML fallback. Add a handler that maps caption/style/rowHeight/columnCount/name onto the backing X14.Slicer element via TryFindSlicerByIndex, mirroring the property vocabulary exposed on Add.
Shape TextBody stores one <a:p> per paragraph; the Get readback flattened all <a:r> descendants into a single string, so multi-line text like 'Line1\nLine2\nLine3' was returned as 'Line1Line2Line3'. Join runs within each paragraph, then join paragraphs with '\n' so multi-line shape text round-trips through Set/Get.
When users pass series{N}.name=Sheet1!A1 the legend/tooltip text was
being written as literal <c:v>Sheet1!A1</c:v>, so Excel displayed the
string 'Sheet1!A1' instead of resolving it to the cell's value. Detect
cell-reference patterns in both Add (ApplySeriesReferences) and Set
(series.name case) and emit <c:tx><c:strRef><c:f>…</c:f></c:strRef></c:tx>
so Excel resolves the cell on open. Reader now also surfaces nameRef
alongside the existing valuesRef/categoriesRef format keys.
… formula pattern Excel expects <x:tableColumn> to carry <x:calculatedColumnFormula> so newly appended rows auto-fill. When Add builds a table over a range where every data row in a column carries a formula with the same relative shape (e.g. =J2*K2, =J3*K3 …), stamp the first formula onto the tableColumn. Detection uses a naive strip-digits heuristic — it matches how users actually author calc'd columns without trying to reimplement Excel's relative-reference normalizer.
The formulacf dxf builder only threaded font.color and font.bold into the dxf Font; font.underline, font.italic, font.strike, font.size, font.name were silently dropped. Extract a BuildFormulaCfFont helper that appends every sub-prop (b, i, u, strike, sz, name/FontName, color) and share it from the formulacf Add path. Users who pass the full set now get a dxf Font that Excel renders correctly.
When title (or axisTitle/catTitle) is passed a single-cell reference like Sheet1!A1, emit <c:tx><c:strRef><c:f>Sheet1!$A$1</c:f></c:strRef></c:tx> instead of a literal <a:t>Sheet1!A1</a:t>. Same fix family as the series name strRef path — BuildChartTitle is shared by chart title and both axis titles, so one call site fans out to all three.
freeze=A1 has colSplit=0 and rowSplit=0, which produced an invalid <pane state="frozen" activePane="topRight"/> with no xSplit/ySplit. Treat A1 as a no-op — any existing pane is already cleared earlier in the branch, so simply break before emitting a new one.
When building a pivot from a source range, resolve each source column's StyleIndex to its numFmtId and stamp it onto the cacheField. Without this, a date-formatted column (numFmtId 164, yyyy-mm-dd) rendered in the pivot as raw OADate serials (45306, 45337, ...) instead of the intended date format. Reuses ResolveColumnNumFmtIds already used for DataField.NumberFormatId.
The chart Set path accepted logBase=N and logScale=true (commit b5caba1) but Add silently dropped yAxisScale/logScale/logBase because they were handled only via the deferred-key path and yAxisScale wasn't registered. Register yaxisscale as a deferred Add key and teach the logbase/logscale setter case to accept 'log' / 'linear' as yAxisScale-style verbs. Add now emits <c:logBase val="10"/> for yAxisScale=log or logScale=true, and <c:logBase val="N"/> for logBase=N, matching Set.
SKILL.md (minimal additions per conciseness rule): - Add `check` to L1 read commands - Note SVG support on Word/Excel/PPT picture - PPT: placeholder type, AddParagraph level/lineSpacing/spaceBefore/After - Word: field types expanded (22 zero-param + 6 parameterized) - Excel: pareto chart, CF types, anchor cell-range syntax, calculatedField README.md: - Excel features: sort (sheet/range/multi-key), pareto + log axis, SVG images, pivot calculatedField, _xlfn. auto-prefix - PowerPoint features: slide HF toggles, pattern fill + blur, hyperlink tooltip + slide-jump, picture fill modes, placeholder add - Word features: SVG images, expanded field types - Command Reference: add `check` command
Text-overflow scanning now emits as DocumentIssue (Format+Warning, Id prefix 'O') from ExcelHandler/PowerPointHandler ViewAsIssues, reusing the existing CheckAllCellOverflow and CheckTextOverflow logic. The standalone 'officecli check' command is removed — users migrate to 'officecli view <file> issues' (optionally '--type format'). Underlying CheckShapeTextOverflow/CheckCellOverflow/CheckAllCellOverflow handler APIs are retained; they still back the inline overflow warning emitted on add/set through the resident server.
Two issues (iOfficeAI#68): 1. MatchesRunSelector fell through to the generic XML attribute path for `color`, which returns the OOXML-stored value without `#`. Comparing against user input `#FF0000` always failed. Normalize both sides (strip `#`, upper-case) to align with the project Color Rules (Add/Set accept `#FF0000`, `FF0000`, named colors). 2. The table branch in Query only descended into cells for OLE and equation selectors; run selectors were skipped, so red runs inside tables were invisible to `query "run[color=#FF0000]"`. Added a run-selector branch that mirrors the existing OLE traversal, emitting /body/tbl[i]/tr[j]/tc[k]/p[m]/r[n] paths.
Contributor
Author
|
Hi @kuishou68 — this PR has merge conflicts with the current main branch. The main branch has advanced significantly (2100+ commits) since this PR was created, and the Word handler files have been extensively refactored (split into domain partials, revision API redesign, etc.). To resolve the conflicts, this branch needs a rebase against the latest main. The key files touched by this PR ( Would you like me to attempt the rebase, or would you prefer to rework this on top of the latest main? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
tracked=truesupport to Word find/replace so matches are emitted asw:del+w:instrackRevisions/trackChangessetting support to explicitly toggle review mode insettings.xmlWhy
This moves tracked review markup generation into OfficeCLI's OpenXML flow instead of requiring callers to patch DOCX ZIP/XML externally. It also makes it possible to exit review mode cleanly after
acceptAllChanges/rejectAllChanges.Validation
dotnet restore src/officecli/officecli.csproj -nologodotnet build src/officecli/officecli.csproj -nologo --no-restore